Old Crypto Server
We found a very old crypto server. He has a secret that we want to get. However, he will not give it up so easily. Flag form for this task is "Aero{[0-9a-f]{32}}"
nc tasks.aeroctf.com 44323
- File: -
Recon
We're given the source code to the python program that's listening on the challenge service. On connection, the program gives us a simple menu with some options:
1. Encrypt
2. Decrypt
3. Get server secret
4. Exit
>
Looking at the source code, options 1 and 2 don't seem useful. However,
with option 3, we're asked to give a "salt" and then we get
$$AESEncrypt_{ECB}(S_k, Pad(Salt|Flag))$$
.
Since this is ECB mode, we know that we can leak the encrypted flag character by character since we control a prefix, which is the salt in this case. We do so by asking the server to give us the server secret with a padding that we control such that the next character we're trying to bruteforce is the last character in the block that contains our prefix.
We used a modified version of brute_ecb_suffix
from p4's crypto commons which takes
into account the fact that the suffix is not a multiple of the AES block size and uses a
different alphabet to speed things up.
Solution
pip install crypto-commons
from pwn import *
from crypto_commons.generic import chunk
conn = remote("tasks.aeroctf.com", 44323)
print(conn.recvuntil("> "))
ALPHABET = [ord(x) for x in "1234567890abcdefAero{}"]
def encrypt(msg):
conn.send_raw("3\n")
conn.recvuntil('salt: ')
conn.send_raw(msg + '\n')
conn.recvuntil(": b'")
encrypted = conn.recvuntil("'")[:-1]
conn.recvuntil("> ")
return b64d(encrypted)
def brute_ecb_suffix(encrypt_function, block_size=16,
expected_suffix_len=32, pad_char='A',
alphabet=list(range(256))):
suffix = ""
padding_len = block_size - (expected_suffix_len % block_size)
padded_suffix_len = expected_suffix_len + padding_len
recovery_block = padded_suffix_len / block_size - 1
alphabet_chars = [chr(x) for x in alphabet]
for i in range(padded_suffix_len - len(suffix) - 1, padding_len-1, -1):
data = pad_char * i
correct = chunk(encrypt_function(data),
block_size)[recovery_block]
for char in alphabet_chars:
test = data + suffix + char
try:
encrypted = chunk(encrypt_function(test),
block_size)[recovery_block]
if correct == encrypted:
suffix += char
print('FOUND', padded_suffix_len - i, char)
break
except Exception as ex:
pass
return suffix
print(brute_ecb_suffix(encrypt,
block_size=16,
expected_suffix_len=38,
pad_char='A',
alphabet=ALPHABET))
Flag
[+] Opening connection to tasks.aeroctf.com on port 44323: Done
1. Encrypt
2. Decrypt
3. Get server secret
4. Exit
>
('FOUND', 1, 'A')
('FOUND', 2, 'e')
('FOUND', 3, 'r')
('FOUND', 4, 'o')
...
Aero{5013a76ed3b98bae1e79169b3495f47a}
Aero{5013a76ed3b98bae1e79169b3495f47a}